#ifndef MATRIX_H
#define MATRIX_H

#include <cstring>

namespace MathGraphics
{
	struct Matrix
	{
		double d[4][4];

		Matrix();
		Matrix(double initd[4][4]);
		Matrix(double d11, double d12, double d13, double d14,
			   double d21, double d22, double d23, double d24,
			   double d31, double d32, double d33, double d34,
			   double d41, double d42, double d43, double d44);

		Matrix inverse() const;

		Matrix operator+(const Matrix& m1) const;
		Matrix& operator+=(const Matrix& m1);
		Matrix operator-(const Matrix& m1) const;
		Matrix& operator-=(const Matrix& m1);
		Matrix operator*(const Matrix& m1) const;
		Matrix& operator*=(const Matrix& m1);

		bool operator==(const Matrix& m1) const;
		bool operator!=(const Matrix& m1) const;
	};

	inline Matrix::Matrix()
	{
		for (int i = 0; i < 4; i++)
		{
			for (int j = 0; j < 4; j++)
			{
				if (i == j)
					d[i][j] = 1.0f;
				else
					d[i][j] = 0.0f;
			}
		}
	}

	inline Matrix::Matrix(double initd[4][4])
	{
		memcpy(d, initd, 16 * sizeof(double));
	}

	inline Matrix::Matrix(double d11, double d12, double d13, double d14,
						  double d21, double d22, double d23, double d24,
						  double d31, double d32, double d33, double d34,
						  double d41, double d42, double d43, double d44)
	{
		d[0][0] = d11;
		d[0][1] = d12;
		d[0][2] = d13;
		d[0][3] = d14;

		d[1][0] = d21;
		d[1][1] = d22;
		d[1][2] = d23;
		d[1][3] = d24;

		d[2][0] = d31;
		d[2][1] = d32;
		d[2][2] = d33;
		d[2][3] = d34;

		d[3][0] = d41;
		d[3][1] = d42;
		d[3][2] = d43;
		d[3][3] = d44;
	}

	inline Matrix Matrix::operator+(const Matrix& m1) const
	{
		MathGraphics::Matrix m2;

		for (int i = 0; i < 4; i++)
		{
			for (int j = 0; j < 4; j++)
			{
				m2.d[i][j] = d[i][j] + m1.d[i][j];
			}
		}

		return m2;
	}

	inline Matrix& Matrix::operator +=(const Matrix& m1)
	{
		for (int i = 0; i < 4; i++)
		{
			for (int j = 0; j < 4; j++)
			{
				d[i][j] += m1.d[i][j];
			}
		}

		return (*this);
	}

	inline Matrix Matrix::operator-(const Matrix& m1) const
	{
		MathGraphics::Matrix m2;

		for (int i = 0; i < 4; i++)
		{
			for (int j = 0; j < 4; j++)
			{
				m2.d[i][j] = d[i][j] - m1.d[i][j];
			}
		}

		return m2;
	}

	inline Matrix& Matrix::operator -=(const Matrix& m1)
	{
		for (int i = 0; i < 4; i++)
		{
			for (int j = 0; j < 4; j++)
			{
				d[i][j] -= m1.d[i][j];
			}
		}

		return (*this);
	}

	inline Matrix Matrix::operator*(const Matrix& m1) const
	{
		double md[4][4] = {0.0f};
		for (int i = 0; i < 4; i++)
		{
			for (int j = 0; j < 4; j++)
			{
				for (int k = 0; k < 4; k++)
				{
					md[k][i] += d[k][j] * m1.d[j][i];
				}
			}
		}

		return Matrix(md);
	}

	inline Matrix& Matrix::operator *=(const MathGraphics::Matrix &m1)
	{
		double md[4][4] = {0.0f};

		for (int i = 0; i < 4; i++)
		{
			for (int j = 0; j < 4; j++)
			{
				for (int k = 0; k < 4; k++)
				{
					md[k][i] += d[k][j] * m1.d[j][i];
				}
			}
		}

		memcpy(d, md, 16 * sizeof(double));
		return (*this);
	}

	
	inline Matrix operator*(double f, const Matrix& m2)
	{
		Matrix m3;

		for (int i = 0; i < 4; i++)
		{
			for (int j = 0; j < 4; j++)
			{
				m3.d[i][j] = f * m2.d[i][j];
			}
		}

		return m3;
	}
	
	inline bool Matrix::operator==(const Matrix& m1) const
	{
		for (int i = 0; i < 4; i++)
		{
			for (int j = 0; j < 4; j++)
			{
				if (d[i][j] != m1.d[i][j])
					return false;
			}
		}

		return true;
	}

	inline bool Matrix::operator!=(const Matrix& m1) const
	{
		for (int i = 0; i < 4; i++)
		{
			for (int j = 0; j < 4; j++)
			{
				if (d[i][j] != m1.d[i][j])
					return true;
			}
		}

		return false;
	}
}

#endif